
<script type="text/x-red" data-template-name="link in">
    <div class="form-row">
        <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
        <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
    </div>
    <div class="form-row node-input-link-row"></div>
</script>
<script type="text/x-red" data-template-name="link out">
    <div class="form-row">
        <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
        <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
    </div>
    <div class="form-row node-input-link-row"></div>
</script>
<script type="text/x-red" data-help-name="link in">
    <p>Create virtual wires between flows.</p>
    <h3>Details</h3>
    <p>The node can be connected to any <code>link out</code> node that exists on any tab.
       Once connected, they behave as if they were wired together.</p>
    <p>The wires between link nodes are only displayed when a link node is selected.
       If there are any wires to other tabs, a virtual node is shown that can be clicked
       on to jump to the appropriate tab.</p>
    <p><b>Note: </b>Links cannot be created going into, or out of, a subflow.</p>
</script>
<script type="text/x-red" data-help-name="link out">
    <p>Create virtual wires between flows.</p>
    <h3>Details</h3>
    <p>The node can be connected to any <code>link in</code> node that exists on any tab.
       Once connected, they behave as if they were wired together.</p>
    <p>The wires between link nodes are only displayed when a link node is selected.
       If there are any wires to other tabs, a virtual node is show that can be clicked
       on to jump to the appropriate tab.</p>
    <p><b>Note: </b>Links cannot be created going into, or out of, a subflow.</p>
</script>
<style>
#node-input-link-container {
    position: relative;
}
#node-input-link-container li {
    padding: 2px 5px;
    background: none;
    font-size: 0.8em;
    margin:0;
    white-space: nowrap;
}
#node-input-link-container li label {
    margin-bottom: 0;
    width: 100%;
}
#node-input-link-container li label input {
    vertical-align: top;
    width:15px;
    margin-right: 10px;
}
#node-input-link-container li:hover,
#node-input-link-container li:hover .node-input-target-node-sublabel {
    background: #f0f0f0;
}
.node-input-link-node-sublabel {
    position:absolute;
    right: 0px;
    padding-right: 10px;
    padding-left: 10px;
    font-size: 0.8em;
}
</style>

<script type="text/javascript">
(function() {

    function sortNodeList(nodeList,sortOn,sortOnSecond) {
        var currentSort = nodeList.data('currentSort');
        var currentSortOrder = nodeList.data('currentSortOrder');

        if (!currentSort) {
            currentSort = sortOn;
            currentSortOrder = 'a';
        } else {
            if (currentSort === sortOn) {
                currentSortOrder = (currentSortOrder === 'a'?'d':'a');
            } else {
                currentSortOrder = 'a';
            }
            currentSort = sortOn;
        }
        nodeList.data('currentSort',currentSort);
        nodeList.data('currentSortOrder',currentSortOrder);

        $("#node-input-link-container-div .fa").hide();
        $(".node-input-link-sort-"+currentSort+"-"+currentSortOrder).show();


        var items = nodeList.find("li").get();
        items.sort(function(a,b) {
            var labelA = $(a).find(".node-input-link-node-"+currentSort).text().toLowerCase();
            var labelB = $(b).find(".node-input-link-node-"+currentSort).text().toLowerCase();
            if (labelA < labelB) { return currentSortOrder==='a'?-1:1; }
            if (labelA > labelB) { return currentSortOrder==='a'?1:-1; }

            if (sortOnSecond) {
                labelA = $(a).find(".node-input-link-node-"+sortOnSecond).text().toLowerCase();
                labelB = $(b).find(".node-input-link-node-"+sortOnSecond).text().toLowerCase();
                if (labelA < labelB) { return currentSortOrder==='a'?-1:1; }
                if (labelA > labelB) { return currentSortOrder==='a'?1:-1; }
            }
            return 0;
        });
        $.each(items, function(i, li) {
            nodeList.append(li);
        });
    }
    function onEditPrepare(node,targetType) {
        if (!node.links) {
            node.links = [];
        }
        node.oldLinks = [];

        $('<div id="node-input-link-container-div" style="min-height: 100px;position: relative;   box-sizing: border-box; border-radius: 2px; height: 180px;  border: 1px solid #ccc;overflow:hidden; ">'+
            '    <div style="box-sizing: border-box; line-height: 20px; font-size: 0.8em; border-bottom: 1px solid #ddd; height: 20px;">'+
            '        <div style="display: inline-block;margin-left: 5px;"><a id="node-input-link-sort-label" href="#" data-i18n="[title]node-red:link.label.sortByLabel"><span data-i18n="node-red:link.label.node">name</span> <i class="node-input-link-sort-label-a fa fa-caret-down"></i><i class="node-input-link-sort-label-d fa fa-caret-up"></i></a></div>'+
            '        <div style="position: absolute; right: 10px; width: 50px; display: inline-block; text-align: right;"><a id="node-input-link-sort-type" href="#" data-i18n="[title]node-red:link.label.sortByFlow"><i class="node-input-link-sort-sublabel-a fa fa-caret-down"></i><i class="node-input-link-sort-sublabel-d fa fa-caret-up"></i> <span data-i18n="node-red:link.label.type">flow</span></a></div>'+
            '    </div>'+
            '    <div style="background: #fbfbfb; box-sizing: border-box; position:absolute; top:20px;bottom:0;left:0px;right:0px; overflow-y: scroll; overflow-x: hidden;">'+
            '        <ul id="node-input-link-container" style=" list-style-type:none; margin: 0;"></ul>'+
            '    </div>'+
            '</div>').appendTo('.node-input-link-row');

        var nodeList = $("#node-input-link-container");
        var candidateNodes = RED.nodes.filterNodes({type:targetType});
        var inSubflow = !!RED.nodes.subflow(node.z);
        candidateNodes.forEach(function(n) {
            if (inSubflow) {
                if (n.z !== node.z) {
                    return;
                }
            } else {
                if (!!RED.nodes.subflow(n.z)) {
                    return;
                }
            }
            var isChecked = false;

            isChecked = (node.links.indexOf(n.id) !== -1) || (n.links||[]).indexOf(node.id) !== -1;

            if (isChecked) {
                node.oldLinks.push(n.id);
            }

            var container = $('<li/>',{class:"node-input-link-node"});
            var row = $('<label/>',{for:"node-input-link-node-"+n.id}).appendTo(container);
            $('<input>',{type:"checkbox",class:"node-input-link-node-checkbox",id:"node-input-link-node-"+n.id})
                .data('node-id',n.id)
                .prop('checked', isChecked)
                .appendTo(row);
            container.on('mouseover',function(e) {
                n.highlighted = true;
                n.dirty = true;
                RED.view.redraw();
            });
            container.on('mouseout',function(e) {
                n.highlighted = false;
                n.dirty = true;
                RED.view.redraw();
            });
            var labelSpan = $('<span>');
            var label = n.name||n.id;
            var sublabel;
            var tab = RED.nodes.workspace(n.z);
            if (tab) {
                sublabel = tab.label||tab.id;
            } else {
                tab = RED.nodes.subflow(n.z);
                sublabel = "subflow : "+tab.name;
            }
            $('<span>',{class:"node-input-link-node-label",style:"white-space:nowrap"}).text(label).appendTo(row);
            if (sublabel) {
                $('<span>',{class:"node-input-link-node-sublabel"}).text(sublabel).appendTo(row);
            }
            container.appendTo(nodeList);
        });

        sortNodeList(nodeList,'sublabel','label');

        $("#node-input-link-sort-label").click(function(e) {
            e.preventDefault();
            sortNodeList(nodeList,'label');
        });

        $("#node-input-link-sort-type").click(function(e) {
            e.preventDefault();
            sortNodeList(nodeList,'sublabel');
        });
    }

    function resizeNodeList() {
        var rows = $("#dialog-form>div:not(.node-input-link-row)");
        var height = $("#dialog-form").height();
        for (var i=0;i<rows.size();i++) {
            height -= $(rows[i]).outerHeight(true);
        }
        var editorRow = $("#dialog-form>div.node-input-link-row");
        height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
        $("#node-input-link-container-div").css("height",height+"px");
    }

    function onEditSave(node) {
        node.links = [];
        $(".node-input-link-node-checkbox").each(function(n) {
            if ($(this).prop("checked")) {
                node.links.push($(this).data('node-id'));
            }
        });
        node.oldLinks.sort();
        node.links.sort();
        var nodeMap = {};
        var length = Math.max(node.oldLinks.length,node.links.length);
        for (var i=0;i<length;i++) {
            if (i<node.oldLinks.length) {
                nodeMap[node.oldLinks[i]] = nodeMap[node.oldLinks[i]]||{};
                nodeMap[node.oldLinks[i]].old = true;
            }
            if (i<node.links.length) {
                nodeMap[node.links[i]] = nodeMap[node.links[i]]||{};
                nodeMap[node.links[i]].new = true;
            }
        }
        var n;
        for (var id in nodeMap) {
            if (nodeMap.hasOwnProperty(id)) {
                n = RED.nodes.node(id);
                if (n) {
                    if (nodeMap[id].old && !nodeMap[id].new) {
                        // Removed id
                        i = n.links.indexOf(node.id);
                        if (i > -1) {
                            n.links.splice(i,1);
                        }
                    } else if (!nodeMap[id].old && nodeMap[id].new) {
                        // Added id
                        i = n.links.indexOf(id);
                        if (i === -1) {
                            n.links.push(node.id);
                        }
                    }
                }
            }
        }
    }

    function onAdd() {
        for (var i=0;i<this.links.length;i++) {
            var n = RED.nodes.node(this.links[i]);
            if (n && n.links.indexOf(this.id) === -1) {
                n.links.push(this.id);
            }
        }
    }

    RED.nodes.registerType('link in',{
        category: 'input',
        color:"#ddd",//"#87D8CF",
        defaults: {
            name: {value:""},
            links: { value: [] }
        },
        inputs:0,
        outputs:1,
        icon: "link-out.png",
        outputLabels: function(i) {
            return this.name||this._("link.linkIn");
        },
        label: function() {
            return this.name||this._("link.linkIn");
        },
        labelStyle: function() {
            return this.name?"node_label_italic":"";
        },
        oneditprepare: function() {
            onEditPrepare(this,"link out");
        },
        oneditsave: function() {
            onEditSave(this);
        },
        onadd: onAdd,
        oneditresize: resizeNodeList
    });

    RED.nodes.registerType('link out',{
        category: 'output',
        color:"#ddd",//"#87D8CF",
        defaults: {
            name: {value:""},
            links: { value: []}
        },
        align:"right",
        inputs:1,
        outputs:0,
        icon: "link-out.png",
        inputLabels: function(i) {
            return this.name||this._("link.linkOut");
        },
        label: function() {
            return this.name||this._("link.linkOut");
        },
        labelStyle: function() {
            return this.name?"node_label_italic":"";
        },
        oneditprepare: function() {
            onEditPrepare(this,"link in");
        },
        oneditsave: function() {
            onEditSave(this);
        },
        onadd: onAdd,
        oneditresize: resizeNodeList
    });
})();
</script>
